package org.dreamwork.assistant.tools.network.gui;

import org.dreamwork.ui.concurrent.Handler;
import org.dreamwork.ui.concurrent.IMessageHandler;
import org.dreamwork.ui.concurrent.Message;
import org.dreamwork.util.Tools;

import javax.swing.*;
import javax.swing.text.*;
import java.awt.*;
import java.io.IOException;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

/**
 * Created by seth.yang on 2018/5/7
 */
public class TextAreaLogger implements IMessageLogger, IMessageHandler, Runnable {
    private JTextPane text;
    private boolean showTime, showAscii, autoReturn = true;
    private Handler handler;
    private BlockingQueue<Object> queue = new ArrayBlockingQueue<> (16);
    private SimpleDateFormat sdf = new SimpleDateFormat ("yyyy-MM-dd HH:mm:ss.SSS");
    private StyledDocument doc;
    private OutputStream out;

    private Style normal, info, error;


    private static final Object BREAK   = new byte[0];
    private static final String CRLF    = "\r\n";

    private static final class MessageWrapper {
        byte[]  message;
        int     level;

        MessageWrapper (byte[] message, int level) {
            this.message = message;
            this.level   = level;
        }
    }

    TextAreaLogger (JTextPane area, boolean showTime, boolean showAscii) {
        this.text = area;
        this.showTime = showTime;
        this.showAscii = showAscii;

        handler = new Handler (this);
        doc = text.getStyledDocument ();

        normal = doc.addStyle ("normal", null);
        StyleConstants.setForeground (normal, Color.black);
        StyleConstants.setFontFamily (normal, "Consolas");

        info = doc.addStyle ("info", normal);
        StyleConstants.setForeground (info, Color.decode ("#666600"));

        error = doc.addStyle ("error", normal);
        StyleConstants.setForeground (error, Color.RED);
    }

    public void setShowTime (boolean showTime){
        this.showTime = showTime;
    }

    public void setShowAscii (boolean showAscii) {
        this.showAscii = showAscii;
    }

    public void setAutoReturn (boolean autoReturn) {
        this.autoReturn = autoReturn;
    }

    @Override
    public void append (byte[] message) {
        append (message, NORMAL);
    }

    @Override
    public void append (byte[] message, int level) {
        queue.offer (new MessageWrapper (message, level));
    }

    @Override
    public void dumpTo (OutputStream out) {
        if (out == null) {
            // cancel dump to file
            if (this.out != null) try {
                this.out.close ();
            } catch (IOException ex) {
                ex.printStackTrace ();
            }
        }
        this.out = out;
    }

    @Override
    public void handleUIMessage (Message message) {
        String line;
        byte[] buff = (byte[]) message.obj;
        if (buff == null || buff.length == 0) {
            return;
        }
        if (showAscii || message.what != NORMAL) {
            line = new String (buff);
        } else {
            line = Tools.toHex (buff);
        }

        if (showTime) {
            line = String.format ("[%s] %s", sdf.format (System.currentTimeMillis ()), line);
        }

        AttributeSet as;
        switch (message.what) {
            case NORMAL :
                as = normal;
                break;
            case INFO:
                as = info;
                break;
            case ERROR:
                as = error;
                break;
            default:
                as = normal;
        }
//        text.append (line);
        try {
            doc.insertString (doc.getLength (), line, as);
            if (autoReturn)
                doc.insertString (doc.getLength (), CRLF, as);
        } catch (Exception ex) {
            // error
            ex.printStackTrace ();
        }
        text.setCaretPosition (text.getDocument ().getLength ());

        if (out != null) try {
            out.write (line.getBytes ());
            out.write (CRLF.getBytes ());
            out.flush ();
        } catch (IOException ex) {
            // ignore
        }
    }

    @Override
    public void handleNonUIMessage (Message message) {

    }

    @Override
    public void run () {
        while (true) {
            try {
                Object o = queue.take ();
                if (o == BREAK) {
                    break;
                }

                MessageWrapper wrapper = (MessageWrapper) o;
                handler.sendUIMessage (wrapper.level, wrapper.message);
            } catch (InterruptedException ex) {
                ex.printStackTrace ();
            }
        }

        System.out.println ("the logger looper bread");
    }
}